package com.lagou.edu.sca.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.jwt.crypto.sign.MacSigner;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import javax.sql.DataSource;

@Configuration
@EnableAuthorizationServer //开启认证服务器功能
public class OauthServerConfiger extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private LagouAccessTokenConvertor lagouAccessTokenConvertor;

    @Autowired
    private DataSource dataSource;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        super.configure(security);
        // 相当于打开endpoints访问接口的开关，这样的话后期我们能够访问该接口
        security
                // 允许客户端表单认证
                .allowFormAuthenticationForClients()
                // 开启端口/oauth/token_key的访问权限（允许）
                .tokenKeyAccess("permitAll()")
                // 开启端口/oauth/check_token的访问权限（允许）
                .checkTokenAccess("permitAll()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        super.configure(clients);
        clients.withClientDetails(createJdbcClientDetailsService());
    }

    @Bean
    public JdbcClientDetailsService createJdbcClientDetailsService(){
        JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
        return jdbcClientDetailsService;
    }

    /**
     * 配置token令牌管理
     *
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        super.configure(endpoints);
        endpoints
                .tokenStore(tokenStore()) //指定token的存储方法
                .tokenServices(authorizationServerTokenServices()) //// token服务的一个描述，可以认为是token生成细节的描述，比如有效时间多少等
                .authenticationManager(authenticationManager) // 指定认证管理器，随后注入一个到当前类使用即
                .allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST);
    }

    private String sign_key = "lagou_123";

    /**
     * 该方法用于创建tokenStore对象（令牌存储对象）
     * token以什么方式存储
     */
    public TokenStore tokenStore(){
        // 这里token的存储方式也有多种，比如数据库、Redis、jwt
        // 这里我们返回内存存储，最终目标是用jwt存储到客户端。
//        return new InMemoryTokenStore();
        // 使用JWT令牌
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    /**
     * 返回JWT令牌转换器（帮助我们生产JWT令牌的）
     * 这里，我们可以把签名秘钥传递进去给转换器对象
     */
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey(sign_key); //签名秘钥
        jwtAccessTokenConverter.setVerifier(new MacSigner(sign_key)); //验证时使用的秘钥，和签名密钥保持一致
        jwtAccessTokenConverter.setAccessTokenConverter(lagouAccessTokenConvertor);
        return jwtAccessTokenConverter;
    }

    /**
     * 该方法用户获取一个token服务对象（该对象描述了token有效期等内容）
     */
    public AuthorizationServerTokenServices authorizationServerTokenServices() {
        // 使用默认实现
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setSupportRefreshToken(true); // 是否开启令牌刷新，上面使用refresh_token刷新令牌这件事是否支持
        defaultTokenServices.setTokenStore(tokenStore());

        // 设置JWT令牌的增强
        defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter());

        // 令牌和刷新令牌是两个令牌，两个令牌作用不同
        // 一般直接生成的令牌有效期是有限的，过期就无效了，无效了怎么办，我们可以刷新令牌，重写获取一个新的令牌
        // 设置令牌有效时间（一般设置为2个小时，这里设置20秒，用于测试观察）
        defaultTokenServices.setAccessTokenValiditySeconds(200); //access_token就是我们请求资源需要携带的令牌
        // 设置刷新令牌的有效时间
        defaultTokenServices.setRefreshTokenValiditySeconds(259200); // 3天
        return defaultTokenServices;
    }

}
